package cluster.old;

import org.apache.log4j.Logger;

import cluster.Clusterable;
import cluster.Duster;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
public class CopyOfLookupTable<E extends Clusterable> {
	

	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(CopyOfLookupTable.class);

	Hashtable<Key, Float> table;
	ArrayList<Key> keys;
	Hashtable<Integer, Boolean> unavailable;
	boolean isSorted = true;
	public CopyOfLookupTable(int size){
		table = new Hashtable<Key, Float>(size + size / 2, 0.8f);
		keys = new ArrayList<Key>();
		unavailable = new Hashtable<Integer, Boolean>(size + size / 2);
		isSorted = true;
	}
	
	public CopyOfLookupTable(ArrayList<Duster<E>> list){
		this(list.size());
		Iterator<Duster<E>> i = list.iterator();
		while(i.hasNext()) {
			if (logger.isDebugEnabled()) {
				logger
						.debug("LookupTable(ArrayList<Duster<E>>) - list="
								+ keys.size());
			}
			addDistances(i.next(), list);
		}
		isSorted = false;
	}
	
	public void addDistances(Duster<E> d, ArrayList<Duster<E>> list){
//		if (logger.isDebugEnabled()) {
//			logger.debug("addDistances(Duster<E>, ArrayList<Duster<E>>) - d="
//					+ d.ID + "size: " + list.size());
//		}
		if(d.lookup == null) d.lookup = this;
		Iterator<Duster<E>> i = list.iterator();
		while(i.hasNext()){
			Duster<E> next = i.next();
			if(d.ID != next.ID)
				addDistance(d, next);
		}
		isSorted = false;
	}
	
	public void addDistance(Duster<E> d1, Duster<E> d2){
		if(d1.ID == d2.ID) return;
		float dist = d1.getDistanceTo(d2);
		Key key = new Key(d1.ID, d2.ID, dist);
		if(table.containsKey(key)) return;
		table.put(key, new Float(dist));
		keys.add(key);
		isSorted = false;
	}
	public void addDistance(Duster<E> d1, Duster<E> d2, float dist){
		if(d1.ID == d2.ID) return;
		Key key = new Key(d1.ID, d2.ID, dist);
		if(table.containsKey(key)) return;
		table.put(key, dist);
		keys.add(key);
		isSorted = false;
	}
	
	public void makeUnavailable(Duster<E> d){
		unavailable.put(new Integer(d.ID), new Boolean(false));
	}
	
	public void removeDuster(Duster<E> d){
//		if (logger.isDebugEnabled()) {
//			logger.debug("removeDuster(Duster<E>) - ID=" + d.ID + " clr: " + d.centroid.toString());
//		}

		Enumeration<Key> keys = table.keys();
		ArrayList<Key> remKeys = new ArrayList<Key>();
		while(keys.hasMoreElements()) {
			Key key = keys.nextElement();
			if(key.id1 == d.ID || key.id2 == d.ID)
				remKeys.add(key);
		}
		Iterator<Key> rem = remKeys.iterator();
		while(rem.hasNext()) {
			Key next = rem.next();
			table.remove(next);
			this.keys.remove(next);
		}
		unavailable.remove(new Integer(d.ID));
		isSorted = false;
	}
	/*
	public Key getClosestLink(){
		Enumeration<Key> keys = table.keys();
		Key closest = null;
		float minDist = Float.MAX_VALUE;
		while(keys.hasMoreElements()){
			Key key = keys.nextElement();
			float dist = table.get(key).floatValue();
			if(dist < minDist){
				closest = key;
				minDist = dist;
			}
		}
		return closest;
	}*/
	
	
	public Key getClosestLink(ArrayList<Duster<E>> list){
		if(!isSorted){
			Collections.sort(keys);
			isSorted = true;
		}
		Iterator<Key> i = keys.iterator();
		boolean found = false;
		Key next = null;
		while(i.hasNext() && !found){
			next = i.next();
			if(!(unavailable.containsKey(new Integer(next.id1)) && unavailable.containsKey(new Integer(next.id2))))
				found = true;
		}
		return next;
	}
	
	public float getDistance(Duster d1, Duster d2){
		return getDistance(new Key(d1.ID, d2.ID));
	}
	
	public float getDistance(Key key){
		if(!table.containsKey(key)) return Float.NaN;
		return table.get(key).floatValue();
	}
	
	class Key implements Comparable{
		/**
		 * Logger for this class
		 */
		private final Logger logger = Logger.getLogger(Key.class);

		int id1, id2;
		float dist;
		boolean available = true;
		public Key(int id1, int id2) {
			super();
			this.id1 = Math.min(id1, id2);
			this.id2 = Math.max(id1, id2);
			this.dist = 0;
		}
		public Key(int id1, int id2, float dist) {
			super();
			this.id1 = Math.min(id1, id2);
			this.id2 = Math.max(id1, id2);
			this.dist = dist;
		}
		
		

		@Override
		public int hashCode() {
			final int PRIME = 31;
			int result = 1;
			result = PRIME * result + id1;
			result = PRIME * result + id2;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			final Key other = (Key) obj;
			if (id1 != other.id1)
				return false;
			if (id2 != other.id2)
				return false;
			return true;
		}
		public int compareTo(Object o) {
			Key b = (Key)o;
			if(b.dist < dist) return 1;
			if(b.dist > dist) return -1;
			return 0;
		}
		
	}
}
